// 导入vuex
import { createStore, Commit } from "vuex";
import axios, { AxiosRequestConfig } from "axios";
import { arrToObj, objToArr } from "./helper";

// 新建一个response数据的类
// 因为每次请求回来的最外层的格式是固定的，所以可以用
// P={} 会报错 想要默认值为空的对象 根据提示消息 改成`Record<string, never>`
export interface ResponseType<P = `Record<string, never>`> {
  code: number;
  msg: string;
  // data是传入的泛型 每个都不一样
  data: P;
}

// 封装一个异步请求+commit函数
// 这边的这个函数实际上是支持多种不同类型的请求然后重构成同一个函数而来的
// AxiosRequestConfig 是关于axios请求的配置选项
const asyncAndCommit = async (
  url: string,
  mutationName: string,
  commit: Commit,
  config: AxiosRequestConfig = { method: "get" },
  extraData?: any
) => {
  const { data } = await axios(url, config);
  if (extraData) {
    commit(mutationName, { data, extraData }); // 如果有多余的数据就一并commit到对应的mutation中
  } else {
    commit(mutationName, data);
  }
  return data; // 这边返回一个promise对象包裹的值
};
// 用户
export interface UserProps {
  isLogin: boolean;
  nickName?: string;
  _id?: string;
  column?: string;
  email?: string;
  avatar?: ImageProps;
  description?: string;
}

// 文章的类
export interface PostProps {
  _id?: string;
  title: string;
  content?: string;
  excerpt?: string;
  image?: ImageProps | string;
  createdAt?: string; // 时间
  column: string; // 把文章和column联系起来的最重要的属性
  author?: string | UserProps;
  isHTML?: boolean;
}

export interface ImageProps {
  _id?: string;
  url?: string;
  fitUrl?: string;
  createdAt?: string;
}

// 文章和column是一对多的关系
export interface ColumnProps {
  _id: string;
  title: string;
  avatar?: ImageProps;
  description: string;
}

// GlobalPostsProps
export interface GlobalPostsProps {
  data: ListProps<PostProps>;
  loadedColumns: ListProps<{ total: number; currentPage: number }>; // 这里的id用的是currentid
}

// 重构工作：包装成对象的形式
interface ListProps<P> {
  [id: string]: P; // key为[]表示 可索引的类型
}

// 报错信息
export interface GlobalErrorProps {
  status: boolean;
  message?: string;
}

// 确定整个store的类型 定义其泛型
// 由于store中的state中的columns和posts在testData的文件中有约束泛型，所以这里定义GlobalDataProps的时候加以约束
export interface GlobalDataProps {
  error: GlobalErrorProps;
  token: string;
  loading: boolean;
  columns: { data: ListProps<ColumnProps>; currentPage: number; total: number }; // 添加响应式对象total
  posts: GlobalPostsProps; // loadedColumns通过listprops来封装成键值对的形式
  user: UserProps;
}

// store
const store = createStore<GlobalDataProps>({
  state: {
    error: { status: false },
    token: localStorage.getItem("token") || "", // 获取localStorage中是否含有token字段
    loading: false,
    // columns: {}, // 重构为对象
    columns: { data: {}, currentPage: 0, total: 0 },
    // posts: {}, // 重构为对象
    posts: { data: {}, loadedColumns: {} },
    user: { isLogin: true }
  },
  mutations: {
    createPost(state, newPost) {
      // 传递参数payload
      state.posts.data[newPost._id] = newPost;
      // state.posts.push(newPost);
    },
    fetchColumns(state, rawData) {
      const { data } = state.columns;
      const { list, count, currentPage } = rawData.data; // 此时会多增一个count,请求返回结果中获得总的columns数量
      state.columns = {
        data: { ...data, ...arrToObj(list) }, // 注意这里的data是增加而不是代替(增加请求回来下一页的columns)
        total: count,
        currentPage: currentPage * 1 // 可能是string类型 做一下类型转换
      };
      // state.columns.data = arrToObj(rawData.data.list); // 转换成对象的形式
      // state.columns.isLoaded = true; // 获取到columns的信息的时候就设置为true
    },
    fetchColumn(state, rawData) {
      state.columns.data[rawData.data._id] = rawData.data;
      // state.columns = [rawData.data];
    },
    fetchPosts(state, { data: rawData, extraData: columnId }) {
      // 使用展开运算符 使用替换操作会使得data被不用发送请求的空rawData所代替
      // state.posts.data = rawData.data.list;
      const { count, currentPage } = rawData.data;
      state.posts.loadedColumns[columnId] = {
        total: count,
        currentPage
      };
      state.posts.data = {
        ...state.posts.data,
        ...arrToObj(rawData.data.list)
      };
      // 加载过的id要放入loadedColumn中
      // state.posts.loadedColumns.push(columnId);
      // console.log(state.posts.loadedColumns);
    },
    fetchPost(state, rawData) {
      state.posts.data[rawData.data._id] = rawData.data;
    },
    updatePost(state, { data }) {
      state.posts.data[data._id] = data;
      // state.posts = state.posts.map(post => {
      //   if (post._id === data._id) {
      //     return data;
      //   } else {
      //     return post;
      //   }
      // });
    },
    deletePost(state, { data }) {
      // 传递参数payload
      // state.posts = state.posts.filter(post => post._id !== data._id);
      delete state.posts.data[data._id];
    },
    setLoading(state, status) {
      state.loading = status;
    },
    login(state, rawData) {
      const { token } = rawData && rawData.data;
      state.token = token;
      localStorage.setItem("token", token);
      axios.defaults.headers.common.Authorization = `Bearer ${token}`;
    },
    logout(state) {
      state.token = "";
      state.user = { isLogin: false };
      localStorage.removeItem("token");
      delete axios.defaults.headers.common.Authorization;
    },
    fetchCurrentUser(state, rawData) {
      state.user = { isLogin: true, ...rawData.data };
    },
    setError(state, e: GlobalErrorProps) {
      state.error = e;
    },
    updateUser(state, { data }) {
      state.user = { isLogin: true, ...data };
    },
    updateColumn(state, { data }) {
      state.columns.data[data._id] = data;
    }
  },
  actions: {
    fetchColumns({ state, commit }, params = {}) {
      const { currentPage = 1, pageSize = 6 } = params;
      // if (!state.columns.isLoaded) {
      //   // 如果已经加载了就不再发送请求
      //   return asyncAndCommit(
      //     `/columns?currentPage=${currentPage}&pageSize=${pageSize}`,
      //     "fetchColumns",
      //     commit
      //   );
      // }
      // 如果是小于请求参数params中的currentPage的时候就发送
      if (state.columns.currentPage < currentPage) {
        return asyncAndCommit(
          `/columns?currentPage=${currentPage}&pageSize=${pageSize}`,
          "fetchColumns",
          commit
        );
      }
    },

    fetchColumn({ state, commit }, cid) {
      if (!state.columns.data[cid]) {
        return asyncAndCommit(`/columns/${cid}`, "fetchColumn", commit);
      }
      // axios.get(`/columns/${cid}`).then(res => {
      //   commit("fetchColumn", res.data);
      // });
    },
    // actions不能添加两个参数。。得包装成一个才行
    fetchPosts({ state, commit }, params = {}) {
      const { cid, extraData, currentPage = 1, pageSize = 3 } = params;
      // 这里传入cid是初始化的时候传入的，后面通过loadMore函数传入是使用extraData来传的
      const _cid = cid ? cid : extraData;
      if (
        !state.posts.loadedColumns[_cid] ||
        state.posts.loadedColumns[_cid].currentPage < currentPage
      ) {
        return asyncAndCommit(
          `/columns/${_cid}/posts?currentPage=${currentPage}&pageSize=${pageSize}`,
          "fetchPosts",
          commit,
          { method: "get" },
          _cid
        );
      }

      // axios.get(`/columns/${cid}/posts`).then(resp => {
      //   commit("fetchPosts", resp.data);
      // });
    },
    fetchPost({ state, commit }, id) {
      const currentPost = state.posts.data[id];
      if (!currentPost || !currentPost.content) {
        // 这里如果之前发送的请求回来的数据是不包含content的，说明是文章列表页发送的请求，而进入文章详情页应该再发送一次（返回的数据就会包含content）
        return asyncAndCommit(`/posts/${id}`, "fetchPost", commit);
      } else {
        return Promise.resolve({ data: currentPost }); // 手动发送promise
      }
    },
    login({ commit }, payload) {
      return asyncAndCommit("/user/login", "login", commit, {
        method: "post",
        data: payload
      });
    },
    createPost({ commit }, payload) {
      return asyncAndCommit("/posts", "createPost", commit, {
        method: "post",
        data: payload
      });
    },

    fetchCurrentUser({ commit }) {
      return asyncAndCommit("/user/current", "fetchCurrentUser", commit);
    },
    updateColumn({ commit }, { id, payload }) {
      return asyncAndCommit(`/columns/${id}`, "updateColumn", commit, {
        method: "patch",
        data: payload
      });
    },
    updateUser({ commit }, { id, payload }) {
      return asyncAndCommit(`/user/${id}`, "updateUser", commit, {
        method: "patch",
        data: payload
      });
    },
    loginAndFetch({ dispatch }, loginData) {
      return dispatch("login", loginData).then(() => {
        return dispatch("fetchCurrentUser");
      });
    },
    updatePost({ commit }, { id, payload }) {
      return asyncAndCommit(`/posts/${id}`, "updatePost", commit, {
        method: "patch",
        data: payload
      });
    },
    deletePost({ commit }, id) {
      return asyncAndCommit(`/posts/${id}`, "deletePost", commit, {
        method: "delete"
      });
    }
  },
  getters: {
    getColumnById: state => (id: string) => {
      // console.log(state.columns);
      // return state.columns.find(c => c._id === id);
      return state.columns.data[id];
    },
    getPostsByCId: state => (cid: string) => {
      // return state.posts.filter(post => post.column === cid);
      return objToArr(state.posts.data).filter(post => post.column === cid); // 这边先把state.posts变成数组之后再进行下一步的filter操作
    },
    getLoadedColumnsTotalByCId: state => (cid: string) => {
      return (
        (state.posts.loadedColumns[cid] &&
          state.posts.loadedColumns[cid].total) ||
        0
      );
    },
    getLoadedColumnsCurrentPageByCId: state => (cid: string) => {
      return (
        (state.posts.loadedColumns[cid] &&
          state.posts.loadedColumns[cid].currentPage) ||
        1
      );
    },
    getCurrentPost: state => (id: string) => {
      return state.posts.data[id];
    },
    getColumns: state => {
      return objToArr(state.columns.data);
    }
  }
});

export default store;
